#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#include "fat32.h"
#include "debug.h"

fs_t fs;

enum DIR_ENTRY_ATTR_BIT
{
	READ_ONLY = 0,
	HIDDEN,
	SYSTEM,
	VOLUME_ID,
	DIRECTORY,
	ARCHIVE
};

#define FILE_DIR_ENT_bm  0b00011110 // Only care about HIDDEN FILE, SYSTEM FILE, VOLUME ID, and SUBDIRECTORY bits of attr field
#define FILE_DIR_ENT 0x00			// A directory entry of a normal file should be all zeros (after bit-mask has been applied)
#define UNUSED_DIR_ENT 0xE5
#define END_OF_DIR 0x00

/****************************************************************************
* Purpose:	Initalizes the fat filesystem
* Input:	No Input
* Output:   Returns a value from ERROR_CODES enumeration indicating fatfs
*			status (INIT_SUCCESS on success)
****************************************************************************/
int fatfs_init(fs_read in)
{
	fatvol_t vol;

	// Setup the fs structure -> initalize values, etc
	fs.is_initalized = 0;

	// Make sure we are at song position 0 --> first song in root director
	fs.current_song_num = 0;

	fs.fat_io.sector_buffer.address = INVALID_CLUSTER;

	memset(fs.fat_io.sector_buffer.buf,0,512);

	fs.fat_io.read_sector = in;

	if (!fs.fat_io.read_sector)
	{
		// Ensure that both function pointers are valid
		return INVALID_FS_IO_FUNC_PTR;
	}

	if (!fs.fat_io.read_sector(0,fs.fat_io.sector_buffer.buf))
	{
		// If we can't read the MBR then there is no point in continuing
		return SECTOR_READ_ERROR;
	}

	if (fs.fat_io.sector_buffer.buf[SECTOR_SIG] != 0x55 || fs.fat_io.sector_buffer.buf[SECTOR_SIG+1] != 0xAA)
	{
		// If the sector doesn't end correctly don't continue;
		return INVALID_MBR_SECTOR_SIG;
	}

	//printf("Sector Sig = %x%x\r\n",fs.fat_io.sector_buffer.buf[SECTOR_SIG],fs.fat_io.sector_buffer.buf[SECTOR_SIG+1]);

	fs.mbr.type_code = fs.fat_io.sector_buffer.buf[450];
	//printf("Type Code: %u\r\n", fs.mbr.type_code);

	// Check the partition type of first partition entry in the MBR
	switch(fs.mbr.type_code)
	{
		case 0x0B:
		case 0x06:
		case 0x0C:
		case 0x0E:
		case 0x0F:
		case 0x05:
			fs.mbr.type_code = 1;
			break;
		case 0x00:
			fs.mbr.type_code = 0;
			break;
		default:
			if (fs.mbr.type_code <= 0x06)
				fs.mbr.type_code = 1;
		break;
	}

	if (fs.mbr.type_code)
	{
		// Get cluster number of first cluster of first volume from buffer
		convert2int(0x1BE + 0x08, 4, &(fs.mbr.lba_begin));
		// Get the number of sectors in the volume from buffer
		convert2int(0x1BE + 0x0C, 4, &(fs.mbr.sector_count));

		//printf("Valid Parition\r\nLBA = %lu\r\n", fs.mbr.lba_begin);
	}

	else
	{
		return FS_INIT_READ_SECTOR_AT_LBA_ERROR;
		//printf("May be a MBR Less Disk\r\n");
	}

	if (!fs.fat_io.read_sector(fs.mbr.lba_begin,fs.fat_io.sector_buffer.buf))
	{
		fs.fat_io.sector_buffer.address = fs.mbr.lba_begin;
		return FS_INIT_READ_SECTOR_AT_LBA_ERROR;
	}

	// get info from sector pointed to by lba
	populate_vol_info(&vol);

	// Copy relevant data from volume info sector and store for permanent use
	fs.loc.bytes_per_sector = vol.bytes_per_sector;
	fs.loc.FAT_count = vol.num_of_fat;
	fs.loc.root_cluster = vol.root_cluster;
	fs.loc.sectors_per_fat = vol.sector_per_fat;
	fs.loc.root_sectors_count = vol.root_sector_count;
	fs.loc.sectors_per_cluster = vol.sectors_per_cluster;

	// First LBA (sector) address of FAT, after the reserved space
	fs.loc.FAT_start_sector = fs.mbr.lba_begin + vol.reserved_sector_count;

	// First LBA (sector) address of data portion of volume
	fs.loc.data_start_sector = fs.mbr.lba_begin + vol.reserved_sector_count + (vol.num_of_fat * vol.sector_per_fat);//+ vol.num_of_fat*vol.sector_per_fat;

	// ensure the sectors are 512 bytes long
	if (fs.loc.bytes_per_sector != 512)
	{
		//printf("Sector Size = %u\r\n",fs.loc.bytes_per_sector);

		return UNSUPPORTED_SECTOR_BYTE_SIZE_ERROR;
	}

	// Pre-determine number of 32bit entries there are per fat sector...could be hard coded (like byte_per_sector) but this will allow
	// easier expansion for to a more accommodating FAT32 driver
	fs.loc.entries_per_fat_sector = (fs.loc.bytes_per_sector * 8) / 32;
	// If sector does not end correctly
	if (fs.fat_io.sector_buffer.buf[0x1FE] != 0x55 || fs.fat_io.sector_buffer.buf[0x1FE + 1] != 0xAA)
	{
		//printf("Sector Sig = %x%x", fs.fat_io.sector_buffer.buf[0x1FE], fs.fat_io.sector_buffer.buf[0x1FE + 1]);
		return INVALID_SECTOR_SIG;
	}
	fs.is_initalized = 1;

	return INIT_SUCCESS;
}


/****************************************************************************
* Purpose:	Disables the fat lib
* Input:	No Input
* Output:   No Output
****************************************************************************/
void fatfs_disable(void)
{
	fs.is_initalized = 0;
	return;
}
/****************************************************************************
* Purpose:	Lists all the files in the root dir
* Input:	No Input
* Output:   Not Output
****************************************************************************/
void fat_ls_root_dir(void)
{
	const uint8_t entries_per_sector = fs.loc.bytes_per_sector / 32; //(Bytes per sector(variable) / bytes per entry(32))

	uint8_t x; // Used as entry counter (max of 512-bytes-per-sector / 32-bytes-per-dir-entry) = 16 Entries per sector
	uint32_t k; // Used as a regular counter

	FAT_FILE_t root_dir;

	root_dir.byte_index = 0;
	root_dir.cluster_index = 0;
	root_dir.sector_index = 0;
	root_dir.flags = FAT_FILE_GOOD;

	root_dir.start_cluster = root_dir.current_cluster = fs.loc.root_cluster; //Usually cluster 0x02

	root_dir.size = 0xFFFFFFFF; //The length of director is unknown

	struct fatfs_dir_entry * next_entry;

	if (!fs.fat_io.read_sector((root_dir.start_cluster-2)*fs.loc.sectors_per_cluster + fs.loc.data_start_sector, root_dir.buf))
	{
		printf("Error reading sector\r\n");
		return;
	}

	// Open all sectors of boot_sector from 0 to root_sector_count
	while(root_dir.current_cluster != FS_LAST_CHAIN && root_dir.flags == FAT_FILE_GOOD && fs.is_initalized)
	{
		if (root_dir.flags != FAT_FILE_GOOD)
		{
			return;
		}

		for (x = 0; x < entries_per_sector; x++)
		{
			if (root_dir.buf[x*32] == END_OF_DIR)
			{
				return;
			}

			// If the first byte of entry is 0x35 then this is an unused directory (result of file deletion)
			if ((root_dir.buf[x*32]) != UNUSED_DIR_ENT)
			{
				// Otherwise we have a regular directory entry, therefore continue with entry interp process

				// Populate entry with next entry (j*32) is the address of next directory -> used to pack structure
				next_entry = (struct fatfs_dir_entry*)(root_dir.buf+(x*32));

				// If this directory entry has non-normal attributes, ignore it and move on
				if ((next_entry->Attr & FILE_DIR_ENT_bm) == FILE_DIR_ENT)
				{
					// Print the short-filename version of filename (Name = first 8 bytes)
					for (k = 0; k < 8; k++)
						printf("%c",(*next_entry).Name[k]);

					// Print period between filename and extenstion
					printf(".");

					// Finish printing the entire name *Note: k is untouch from last for loop so it is at valid position 8, hence no initialization*
					for (k = 8; k < 11; k++)
					{
						printf("%c",(*next_entry).Name[k]);
					}
					printf("........SIZE: %lu kB\r\n", next_entry->FileSize/1024);
				}
			}
		}
		fat_file_buffer_next_sector(&root_dir);
	}
}

/****************************************************************************
* Purpose:	Seeks to Beginning or End minus offset (not a true implementation
*			of fseek)
* Input:	Valid Fileptr, offset value (if seeking to end), and which end to
*			seek to (1 = END, 0 = BEGIN)
* Output:   Returns 0 on success, non-zero on failure
****************************************************************************/
int fat_fseek(FAT_FILE_t *fp, uint32_t offset, uint8_t pos)
{
	uint32_t desired_loc;
	uint32_t cluster2read;
	uint32_t sectors2read;
	uint32_t i;

	if (!fp)
		return -1;

	if (fp->flags == FAT_FILE_ERR)
		return -1;

	// If we are seeking to END
	if (pos == 1)
	{
		// Reset the file to beginning of file
		fp->current_cluster = fp->start_cluster;

		fp->byte_index = fp->cluster_index = fp->sector_index = 0;

		// Reset the flags
		fp->flags = FAT_FILE_GOOD;

		// Find the desired location in terms of bytes
		desired_loc = fp->size - offset;

		// Determine how many sectors away that is
		sectors2read = desired_loc / fs.loc.bytes_per_sector;
		// Determine how many clusters away that is
		cluster2read = sectors2read / fs.loc.sectors_per_cluster;

		// Move thru cluster chain until we have reached the desired cluster
		for (i = 0; i < cluster2read; i++)
		{
			fp->current_cluster = fat_find_next_cluster(fp->current_cluster);
			fp->cluster_index++;
		}

		// Set the file's sector_index to the actual sector in the current cluster we need to buffer
		fp->sector_index = sectors2read % fs.loc.sectors_per_cluster;

		// Buffer in said sector
		if (!fs.fat_io.read_sector(fs.loc.data_start_sector + ( (fp->current_cluster - 2) * (fs.loc.sectors_per_cluster) + fp->sector_index), (uint8_t*)fp->buf) )
		{
			//printf("Error reading in buffer, sector val = %lu\r\n", fs.loc.data_start_sector + ( (fp->start_cluster - 2) * fs.loc.sectors_per_cluster ) );
			fp->flags = FAT_FILE_ERR;
			return -1;
		}

		// Set the byte index to the desired byte location
		fp->byte_index = desired_loc;

		if (fp->byte_index == fp->size)
		{
			fp->flags = FAT_FILE_EOF;
		}

		return 0;
	}
	// If we are seeking to beginning
	else if (pos == 0)
	{
		// Point the file to beginning of file
		fp->current_cluster = fp->start_cluster;
		fp->byte_index = fp->cluster_index = fp->sector_index = 0;

		// Reset the file flags to good, since we are at the beginning
		fp->flags = FAT_FILE_GOOD;

		// Buffer in the first sector just as we would if we were opening the file for the first time
		if (!fs.fat_io.read_sector(fs.loc.data_start_sector + ( (fp->start_cluster - 2) * (fs.loc.sectors_per_cluster) ), (uint8_t*)fp->buf) )
		{
			//printf("Error reading in buffer, sector val = %lu\r\n", fs.loc.data_start_sector + ( (fp->start_cluster - 2) * fs.loc.sectors_per_cluster ) );
			fp->flags = FAT_FILE_ERR;
			return -1;
		}

		return 0;
	}
	// If not seeking to beginning or end, then what the hell are we seeking to??? Not a valid type of seek
	return -1;
}

/****************************************************************************
* Purpose:	Closes a file. Free fileptr memory
* Input:	No Input
* Output:   No Output
****************************************************************************/
void fat_fclose(FAT_FILE_t * fp)
{
	//ensure ptr is valid
	if (fp)
	{
		free(fp);
	}

	return;
}

/****************************************************************************
* Purpose:	Reads up to "num_bytes" from fileptr "fp" and places data in
*			"buf"
* Input:	A destination buffer "buf", the number of bytes to read
*			"num_bytes" and the fileptr "fp"
* Output:   Returns the number of bytes read
****************************************************************************/
int	fat_fread(void * buf, uint16_t num_bytes, FAT_FILE_t * fp)
{
	uint16_t i;

	// Ensure that we have valid buffer and fileptr
	if (buf == NULL || fp == NULL)
	{
		//printf("Invalid filePtr, buf pointer or zero read byte count\r\n");
		return 0;
	}

	// Check file flags before moving on
	if ( FAT_FILE_GOOD != (fp->flags) )
	{
		// If the file is at EOF or is in fail state don't attempt to read
		printf("File Flag is either EOF or ERR\r\n");
		return 0;
	}

	// There is a request for more bytes then there are left in file only allow set amount to read to size - byte_index
	if ( num_bytes > ( fp->size - fp->byte_index ) )
		num_bytes = (fp->size - fp->byte_index);

	// If we are at the end of file indicate it
	if (fp->size == fp->byte_index)
		fp->flags = FAT_FILE_EOF;

	// For the true number of bytes to read, buffer them into "buf"
	for ( i = 0; i < num_bytes; i++)
	{
		// Load what is already buffered into fp->buf into "buf" byte by byte
		( (uint8_t*) buf )[i] = fp->buf[ ( fp->byte_index % 512 ) ];

		// Increment the byte index to keep up with everything
		fp->byte_index++;

		// If at the end of a sector, buffer in next sector
		if ( (fp->byte_index % fs.loc.bytes_per_sector) == 0 )
		{
			fat_file_buffer_next_sector(fp);

			// Check to make sure the file is still valid
			if (fp->flags != FAT_FILE_GOOD)
				break;
		}
	}

	// Check to see if we are at EOF now
	if (fp->size == fp->byte_index)
		fp->flags = FAT_FILE_EOF;

	return i;
}

/****************************************************************************
* Purpose:	Opens a file
* Input:	Short filename (8.3 Filename supported only)
* Output:   Returns a fileptr if file is found, or NULL if not found
****************************************************************************/
FAT_FILE_t * fat_fopen(uint8_t * filename)
{
	uint8_t i;
	FAT_FILE_t * fp;

	if (filename != NULL) // Only do this if we have a valid filename ptr
	{
		// Allocate a memory for new file structure
		fp = (FAT_FILE_t *) calloc(1, sizeof( FAT_FILE_t ) );

		if (!fp) // If we failed to allocate memory (malloc return NULL) then return failure (NULL)
		{
			return NULL;
		}

		// Initalize file to error state and clear FAT Cluster values
		fp->flags = FAT_FILE_ERR;
		fp->start_cluster = fp->current_cluster = 0;

		// Copy the filename into the file
		for (i = 0; i < 11; i++)
		{
			fp->filename[i] = filename[i];
			fp->filename[11] = '\0';
		}

		// Save the start_cluster and current_cluster = first cluster of found file
		if (fat_open_file(fp) != 0)
		{
			free(fp);
			return NULL;
		}

		fp->cluster_index = 0;
		fp->sector_index = 0;
		fp->byte_index = 0;

		if (!fs.fat_io.read_sector(fs.loc.data_start_sector + ( (fp->start_cluster - 2) * (fs.loc.sectors_per_cluster) ), (uint8_t*)fp->buf) )
		{

			free(fp);
			return NULL;
		}
		// Change the file flags to valid
		fp->flags = FAT_FILE_GOOD;

		return fp;
	}

	printf("Invalid filename\r\n");

	return NULL;
}

/****************************************************************************
* Purpose:	Finds the next cluster in cluster chain via current cluster index
* Input:	Current cluster index number
* Output:   Next cluster index number or End of Cluster Chain value
****************************************************************************/
uint32_t fat_find_next_cluster(uint32_t current_cluster)
{
	uint32_t next_cluster; // Next cluster value within File Alloc Table
	uint32_t fat_entry;

	// Cluster 0 == ".." so set to root dir since we do not handle sub directories
	if (current_cluster == 0)
	{
		current_cluster = 2;
		printf("Current Cluster equals\r\n");
		return FS_LAST_CHAIN;

	}

	// Find the number of sectors into the FAT we are (clusters/entries_per_fat_sector)->512bytes per sector, 4 bytes per entry so 512/4 = 128 entries per FAT sector
	fs.fat_io.sector_buffer.address = (uint32_t) (current_cluster / fs.loc.entries_per_fat_sector) + fs.loc.FAT_start_sector;

	// Find the value contained at current_cluster value in the File Alloc Table, to see where it points next
	if(fs.fat_io.read_sector( fs.fat_io.sector_buffer.address, (uint8_t*)fs.fat_io.sector_buffer.buf) )
	{
		fat_entry = (current_cluster - ( (fs.fat_io.sector_buffer.address - fs.loc.FAT_start_sector) * 128) ) * 4; // Find relative cluster number within FAT sector
			// There are 128 entries per sector, so the absolute cluster num -> "current cluster" modulo 128 gives us the relative cluster num

		convert2int(fat_entry, 4, &next_cluster);

		// Upper 4 bits are reserved so mask off
		next_cluster &= 0x0FFFFFFF;

		// If the entry is one of the end of chain values return end of chain
		if (next_cluster >= 0x0FFFFFF8 && next_cluster <= 0x0FFFFFFF)
			return FS_LAST_CHAIN;
	}

	else
	{
		//printf("Error reading fat sector\r\n");
		return FS_LAST_CHAIN;
	}

	return next_cluster;
}

/****************************************************************************
* Purpose:	Low level file open function
* Input:	Valid fileptr with filename pre-loaded
* Output:   Return 0 on success, -1 on failure
****************************************************************************/
int fat_open_file(FAT_FILE_t *fp)
{
	if (!fp)
		return -1;

	uint32_t offset;
	uint32_t j = 0;
	const uint8_t entries_per_sector = fs.loc.bytes_per_sector / 32; //(Bytes per sector(variable) / bytes per entry(32))

	uint32_t i = 0; // Used as sector counter
	uint8_t x; // Used as entry counter (max of 512-bytes-per-sector / 32-bytes-per-dir-entry) = 16 Entries per sector

	uint8_t buf[512];

	struct fatfs_dir_entry * next_entry;

	offset = fs.loc.root_cluster;

	for(j = 1; j != 0; )
	{
		// For each sector in cluster
		for (i = 0; i < fs.loc.sectors_per_cluster; i++)
		{
			// Attempt to buffer sector within data region at ((current_cluster of dir * sectors_per_cluster) + sector_in_cluster_offset)
			if (!fs.fat_io.read_sector(fs.loc.data_start_sector + ((offset-2) * (fs.loc.sectors_per_cluster))+ i, (uint8_t*)buf))
			{
				printf("Error reading sector\r\n");
				fp->flags = FAT_FILE_ERR;
				return -1;
			}

			for (x = 0; x < entries_per_sector; x++)
			{
				if (buf[x*32] == END_OF_DIR)
				{
					printf("NO MORE MP3 FILES LEFT\r\n");
					fp->flags = FAT_FILE_ERR;
					return -1;
				}

				// If the first byte of entry is 0x35 then this is an unused directory (result of file deletion)
				if ((buf[x*32]) != UNUSED_DIR_ENT)
				{
					// Otherwise we have a regular directory entry, therefore continue with entry interp process

					// Populate entry with next entry (j*32) is the address of next directory -> used to pack structure
					next_entry = (struct fatfs_dir_entry*)(buf+(x*32));

					// If this directory entry has non-normal attributes, ignore it and move on
					if ((next_entry->Attr & FILE_DIR_ENT_bm) == FILE_DIR_ENT)
					{
						if (memcmp(fp->filename,(*next_entry).Name,11) == 0)
						{
							fp->size = next_entry->FileSize;
							// Copy first cluster of file

							fp->start_cluster = ((uint32_t)(next_entry->FstClusHI)<<16) | (uint32_t)next_entry->FstClusLO;

							// Only 28bits of value at cluster index are used, mask reserved bits off
							fp->start_cluster &= 0x0FFFFFFF;

							// Current_cluster is the first cluster
							fp->current_cluster = fp->start_cluster;

							//return success
							return 0;
						}
					}
				}
			}
		}

		// If we have reached the end of root dir file chain
		if (offset == FS_LAST_CHAIN)
		{
			printf("Reached end of root dir\r\n");
			return -1;
		}
		// otherwise find the next cluster of root directory
		offset = fat_find_next_cluster(offset);
	}
	return -1;
}

/****************************************************************************
* Purpose:	Buffers in the next sector in file
* Input:	Valid fileptr
* Output:   No Output
****************************************************************************/
void fat_file_buffer_next_sector(FAT_FILE_t * fp)
{
	if (!fp || fp->flags != FAT_FILE_GOOD)
	{
		return;
	}

	if ( fp->sector_index < ( fs.loc.sectors_per_cluster - 1) )
		fp->sector_index++;

	else
	{
		fp->current_cluster = fat_find_next_cluster(fp->current_cluster);

		// If there are no more sectors to read -> something went wrong
		if (fp->current_cluster == FS_LAST_CHAIN)
		{
			//printf("At last cluster in chain\r\n");
			fp->flags = FAT_FILE_EOF;
			return;
		}

		// Copy new valid cluster value into current_cluster, increment our index, and reset sector_index
		fp->cluster_index++;
		fp->sector_index = 0;
	}

	// Buffer in first sector of new cluster

	if(!fs.fat_io.read_sector(fs.loc.data_start_sector + fp->sector_index + (fp->current_cluster - 2) * fs.loc.sectors_per_cluster , (uint8_t*) fp->buf ) )
	{
		// If we fail to buffer file set file to fail state
		//printf("Failed during sector buffer in fat_buf_next_clu()\r\n");
		fp->flags = FAT_FILE_ERR;
		return;
	}

	return;
}

/****************************************************************************
* Purpose:	Gets a fat_vol info sector and populates a vol structure with
*			it's data
* Input:	Valid vol record pointer
* Output:   No Output
****************************************************************************/
void populate_vol_info(fatvol_t * vol)
{
	uint32_t i;// index;

	//printf("Hex Byte Per Sector = %x%x\r\n", fs.fat_io.sector_buffer.buf[0x0B], fs.fat_io.sector_buffer.buf[0x0C]);

	convert2int(0x0B, 2, &vol->bytes_per_sector);

	vol->sectors_per_cluster = fs.fat_io.sector_buffer.buf[0x0D];

	convert2int(0x0E, 2, &vol->reserved_sector_count);

	vol->num_of_fat = fs.fat_io.sector_buffer.buf[0x10];

	convert2int(0x18, 2, &vol->sectors_per_track);

	convert2int(0x1C, 4, &vol->num_hidden_sectors);

	convert2int(0x20, 4, &vol->total_sectors);

	convert2int(0x24, 4, &vol->sector_per_fat);

	//printf("Actual Root Cluster Number = ");
	//for (i = 0; i < 4; i++)
	//	printf("%x", fs.fat_io.sector_buffer.buf[0x2C + 3 - i]);

	//printf("\r\n");

	convert2int(0x2C, 4, &vol->root_cluster);

	convert2int(0x30, 2, &vol->fs_info);

	convert2int(0x32, 2, &vol->boot_sec_backup);

	// If extended signature is enabled
	if(fs.fat_io.sector_buffer.buf[0x42] == 0x29)
	{
		// Read in the volume name
		for (i = 0; i < 11; i++)
		{
			vol->name[i] = fs.fat_io.sector_buffer.buf[0x42+i];
		}

		// Read in fs type
		for (i = 0; i < 8; i++)
		{
			vol->fs_type[i] =  fs.fat_io.sector_buffer.buf[0x52+i];
		}
	}

	convert2int(0x1fe, 2, &vol->boot_sector_signature);

	convert2int(0x11, 2, &vol->root_sector_count);

	return;
}

/****************************************************************************
* Purpose:	Converts a location in local buffer to int whose width is
*			specified by "count" and places it in dest ptr"buf"
* Input:	Index into buffer value, width of integer (must be power of 2),
			and a voidptr to valid data of equal width
* Output:   No Output
****************************************************************************/
void convert2int(uint32_t begin, uint32_t count, void* buf)
{
	int i;

	// Ensure that the variable we will store number to is valid
	if (!buf || count < 2 || count%2 != 0)
	{
		return;
	}
	switch(count)
	{
		case 2: // uint16_t
			(*(uint16_t *)buf) = 0;
			for (i = 0; i < 2; i++)
			{
				(*(uint16_t *)buf) += fs.fat_io.sector_buffer.buf[begin+i] << (8 * i);
			}
			break;

		case 4: // uint32_t
			(*(uint32_t *)buf) = 0;
			for (i = 0; i < 4; i++)
			{
				(*(uint32_t *)buf) += fs.fat_io.sector_buffer.buf[begin+i] << (8 * i);
			}
			break;

		case 8: // uint64_t
			(*(uint64_t *)buf) = 0;
			for (i = 0; i < 8; i++)
			{
				(*(uint64_t *)buf) += fs.fat_io.sector_buffer.buf[begin+i] << (8 * i);
			}
			break;

		default:
			return;
	}

	return;
}

/****************************************************************************
* Purpose:	Counts the number of mp3 files in the root directory
* Input:	No Input
* Output:   Returns the number of mp3 files in root dir
****************************************************************************/
uint16_t fat_num_of_mp3_file(void)
{
	const uint8_t entries_per_sector = fs.loc.bytes_per_sector / 32; //(Bytes per sector(variable) / bytes per entry(32))

	uint8_t x; // Used as entry counter (max of 512-bytes-per-sector / 32-bytes-per-dir-entry) = 16 Entries per sector
	uint16_t file_count = 0;
	FAT_FILE_t root_dir;

	root_dir.byte_index = 0;
	root_dir.cluster_index = 0;
	root_dir.sector_index = 0;
	root_dir.flags = FAT_FILE_GOOD;

	root_dir.start_cluster = root_dir.current_cluster = fs.loc.root_cluster; //Usually cluster 0x02

	root_dir.size = 0xFFFFFFFF; //The length of director is unknown

	struct fatfs_dir_entry * next_entry;

	// Open all sectors of boot_sector from 0 to root_sector_count
	if (!fs.fat_io.read_sector((root_dir.start_cluster-2)*fs.loc.sectors_per_cluster + fs.loc.data_start_sector, root_dir.buf))
	{
		printf("Error reading sector\r\n");
		return 0;
	}

	while(root_dir.current_cluster != FS_LAST_CHAIN && root_dir.flags == FAT_FILE_GOOD && fs.is_initalized)
	{
		if (root_dir.flags != FAT_FILE_GOOD)
		{
			printf("end of dir\r\n");

			return file_count;
		}

		for (x = 0; x < entries_per_sector; x++)
		{
			if (root_dir.buf[x*32] == END_OF_DIR)
			{
				//printf("End_Of_Dir entry\r\n");
				return file_count;
			}

			// If the first byte of entry is 0x35 then this is an unused directory (result of file deletion)
			if ((root_dir.buf[x*32]) != UNUSED_DIR_ENT)
			{
				// Otherwise we have a regular directory entry, therefore continue with entry interp process

				// Populate entry with next entry (j*32) is the address of next directory -> used to pack structure
				next_entry = (struct fatfs_dir_entry*)(root_dir.buf+(x*32));

				// If this directory entry has non-normal attributes, ignore it and move on
				if ((next_entry->Attr & FILE_DIR_ENT_bm) == FILE_DIR_ENT)
				{
					// Temp string to hold sfn and a null-terminator so we can use strcmp
					uint8_t str[4];

					memcpy(str,(*next_entry).Name+8,3); // COPY file extention

					str[3] = '\0'; // Add null terminator

					// If the filenames match then success
					if (strcmp((char *)str,"MP3") == 0)
					{
						file_count++;
					}
				}
			}
		}
		fat_file_buffer_next_sector(&root_dir);
	}

	return file_count;
}

/****************************************************************************
* Purpose:	Converts a location in local buffer to int whose width is
*			specified by "count"
* Input:	pointer to location in buffer from which to grab value, width of
*			integer (must be power of 2), and a voidptr to valid data of
*			equal width
* Output:   No Output
****************************************************************************/
void convertbuf2int(void * buf, uint32_t count, void* dest)
{
	int i;

	// Ensure that the variable we will store number to is valid
	if (!buf || count < 2 || count%2 != 0)
	{
		return;
	}

	switch(count)
	{
		case 2: // uint16_t
			(*(uint16_t *)dest) = 0;
			for (i = 0; i < 2; i++)
			{
				(*(uint16_t *)dest) += ((uint8_t*)buf)[i] << (8 * i);
			}
			break;

		case 4: // uint32_t
			(*(uint32_t *)dest) = 0;
			for (i = 0; i < 4; i++)
			{
				(*(uint32_t *)dest) += ((uint8_t*)buf)[i] << (8 * i);
			}
			break;

		case 8: // uint64_t
			(*(uint64_t *)dest) = 0;
			for (i = 0; i < 8; i++)
			{
				(*(uint64_t *)dest) += ((uint8_t*)buf)[i] << (8 * i);
			}
			break;

		default:
			return;
	}

	return;
}